package emysql

import (
	"context"
	"fmt"
	"time"

	"git.edaijia-inc.cn/edaijia/ego/elogger"
	"git.edaijia-inc.cn/edaijia/ego/etracer"

	_ "github.com/go-sql-driver/mysql"
	"xorm.io/core"
	"xorm.io/xorm"
)

type EMysqlConfig struct {
	Addr           string `ini:"addr"`
	User           string `ini:"user"`
	Passwd         string `ini:"passwd"`
	DB             string `ini:"db"`
	MaxConn        int    `ini:"maxconn"` //最大连接数, 为0时不限制
	Prefix         string `ini:"prefix"`
	Timeout        int    `ini:"timeout"`         //查询超时时间
	ConnectTimeout int    `ini:"connect-timeout"` //数据库连接超时时间
	Verbose        bool   `ini:"verbose"`         //打印sql
}

type EMysql struct {
	Config *EMysqlConfig
	engine *xorm.Engine
}

func InitEMysql(conf *EMysqlConfig) *EMysql {
	if conf.ConnectTimeout == 0 {
		conf.ConnectTimeout = 5
	}
	if conf.Timeout == 0 {
		conf.Timeout = 5
	}
	connectString := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&loc=Local&timeout=%ds&readTimeout=%ds", conf.User, conf.Passwd, conf.Addr, conf.DB, conf.ConnectTimeout, conf.Timeout)

	engine, err := xorm.NewEngine("mysql", connectString)
	if err != nil {
		elogger.Panic("emysql init error ", err)
	}

	if conf.MaxConn != 0 {
		engine.SetMaxOpenConns(conf.MaxConn)
		engine.SetMaxIdleConns(conf.MaxConn)
	}

	if conf.Prefix != "" {
		tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, conf.Prefix)
		engine.SetTableMapper(tbMapper)
	}

	engine.AddHook(NewEMysqlHook(conf))

	return &EMysql{Config: conf, engine: engine}
}

func (e *EMysql) ESession(ctx interface{}) *xorm.Session {
	sessionContext := context.Background()
	if e.Config.Timeout > 0 {
		sessionContext, _ = context.WithTimeout(sessionContext, time.Duration(e.Config.Timeout)*time.Second)
	}

	if ctx != nil {
		span := etracer.EMysqlSpanStart(ctx, e.Config.Addr, "Mysql/EGO/xorm")
		if span != nil {
			sessionContext = context.WithValue(sessionContext, ctxSpanKeyInstance, span)
		}
	}

	return e.engine.Context(sessionContext)
}

/*****************************
 *          事务处理         *
 *****************************/

// @title开启事务
//事务的span不记录sql
func (e *EMysql) Transaction(ctx interface{}, process func(*xorm.Session) error) error {
	s := e.engine.NewSession()
	defer s.Close()

	span := etracer.EMysqlSpanStart(ctx, e.Config.Addr, "Mysql/EGO/xorm/Transaction")

	err := s.Begin()
	if err != nil {
		if span != nil {
			etracer.EMysqlSpanEnd(span, "", err)
		}
		return err
	}

	err = process(s)

	if err != nil {
		s.Rollback()
		if span != nil {
			etracer.EMysqlSpanEnd(span, "", err)
		}
		return err
	}

	err = s.Commit()
	if span != nil {
		etracer.EMysqlSpanEnd(span, "", err)
	}

	return err
}
